05 配置管理与App Factory
前面几篇文章中,我们写死了不少东西:端口号、Debug模式、Agent模型名……在开发阶段这没问题,但一旦要部署到生产环境,你就需要区分不同的配置——开发环境开Debug,生产环境关Debug;开发用SQLite,生产用MySQL;API Key不能写死在代码里。
这篇文章就来解决两个问题:怎么管理配置和怎么组织应用创建流程。
一、配置基础
Flask的配置就是一个字典,挂在app.config上:
app = Flask(__name__)
# 像字典一样操作
app.config["SECRET_KEY"] = "my-secret-key"
app.config["DEBUG"] = True
# 批量更新
app.config.update(
DEBUG=True,
SECRET_KEY="my-secret-key",
)配置的key必须是大写,这是Flask的约定。小写的key会被忽略。
1.1 常用配置项
| 配置项 | 说明 | 默认值 |
|---|---|---|
DEBUG | 是否开启调试模式 | False |
SECRET_KEY | 用于签名Session等的密钥 | None |
TESTING | 测试模式,异常会直接抛出 | False |
MAX_CONTENT_LENGTH | 请求体最大字节数 | None(不限制) |
对于Agent项目,你可能还需要自定义配置:
app.config.update(
# Flask内置
SECRET_KEY="your-secret-key",
# Agent相关(自定义)
DEFAULT_MODEL="deepseek-v4-flash",
MAX_HISTORY=50,
API_KEY="sk-xxxx",
)二、从文件加载配置
把配置写在代码里不太安全——API Key可能会被提交到Git。更好的做法是把配置放在单独的文件里。
2.1 从Python文件加载
创建一个config.py:
# config.py
SECRET_KEY = "my-secret-key"
DEBUG = True
DEFAULT_MODEL = "deepseek-v4-flash"
MAX_HISTORY = 50然后在应用中加载:
app = Flask(__name__)
app.config.from_pyfile("config.py")from_pyfile会读取Python文件中所有大写名称的变量。
2.2 从对象加载
如果你更喜欢用类来组织配置,可以用from_object:
# config.py
class Config:
SECRET_KEY = "my-secret-key"
DEFAULT_MODEL = "deepseek-v4-flash"
MAX_HISTORY = 50
class DevelopmentConfig(Config):
DEBUG = True
class ProductionConfig(Config):
DEBUG = Falseapp = Flask(__name__)
app.config.from_object("config.DevelopmentConfig")from_object只是读取对象的属性,不会实例化类。所以用类属性就够了。
2.3 从环境变量加载
最安全的方式——配置通过环境变量传入,代码里不出现任何密钥:
# 设置环境变量(FLASK_前缀是固定的)
export FLASK_SECRET_KEY="my-secret-key"
export FLASK_DEFAULT_MODEL="deepseek-v4-flash"
export FLASK_MAX_HISTORY=50app = Flask(__name__)
app.config.from_prefixed_env()加载后,app.config["SECRET_KEY"]就是"my-secret-key"——前缀FLASK_会被自动去掉。
支持嵌套配置,用双下划线分隔:
export FLASK_DATABASE__HOST="localhost"
export FLASK_DATABASE__PORT=5432app.config["DATABASE"]["HOST"] # "localhost"
app.config["DATABASE"]["PORT"] # 54322.4 从JSON/TOML文件加载
也可以用其他格式的配置文件:
import json
import tomllib
# 从JSON加载
app.config.from_file("config.json", load=json.load)
# 从TOML加载
app.config.from_file("config.toml", load=tomllib.load, text=False)三、多环境配置
实际项目中,至少需要两套配置:开发环境和生产环境。用类继承的方式最清晰:
# config.py
class Config:
"""基础配置"""
SECRET_KEY = "change-me-in-production"
DEFAULT_MODEL = "deepseek-v4-flash"
MAX_HISTORY = 50
class DevelopmentConfig(Config):
"""开发环境"""
DEBUG = True
class ProductionConfig(Config):
"""生产环境"""
DEBUG = False
# 配置映射,方便通过名称查找
config_map = {
"development": DevelopmentConfig,
"production": ProductionConfig,
}通过环境变量切换:
import os
from config import config_map
env = os.environ.get("FLASK_ENV", "development")
app.config.from_object(config_map[env])# 开发环境(默认)
python app.py
# 生产环境
FLASK_ENV=production python app.py四、App Factory模式
前面我们一直在用app = Flask(__name__)直接创建应用。这种方式在小项目中没问题,但有几个问题:
- 测试不方便:想测试不同配置,没法创建多个应用实例
- 循环引用:其他模块import
app时容易出问题 - 初始化顺序:扩展和蓝图的注册逻辑散落各处
App Factory(应用工厂)模式就是把创建应用的过程封装到一个函数里:
def create_app():
app = Flask(__name__)
# 加载配置
app.config.from_prefixed_env()
# 注册蓝图
from routes.chat import chat_bp
from routes.session import session_bp
from routes.health import health_bp
app.register_blueprint(chat_bp, url_prefix="/api")
app.register_blueprint(session_bp, url_prefix="/api")
app.register_blueprint(health_bp)
# 注册错误处理
from errors import register_error_handlers
register_error_handlers(app)
return app启动时调用工厂函数:
# app.py
if __name__ == "__main__":
app = create_app()
app.run()4.1 为什么叫"工厂"
因为这个函数就像工厂一样,每次调用都能生产一个新的应用实例。你可以生产不同配置的实例:
# 生产环境的实例
prod_app = create_app()
# 测试用的实例
test_app = create_app()
test_app.config["TESTING"] = True4.2 Flask自动发现
Flask命令行工具能自动识别名为create_app或make_app的工厂函数:
# Flask会自动找到create_app
flask --app app run
# 也可以指定参数
flask --app 'app:create_app(debug=True)' run4.3 配合扩展
App Factory模式下,扩展的初始化要用init_app而不是直接传app:
# 创建扩展对象(不绑定应用)
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
def create_app():
app = Flask(__name__)
app.config.from_prefixed_env()
# 绑定扩展到应用
db.init_app(app)
return app这样同一个扩展对象可以绑定到不同的应用实例。
五、完整项目结构
把App Factory和配置管理结合起来,一个标准的Agent API项目结构:
my-agent-api/
├── app.py # 工厂函数 + 启动入口
├── config.py # 多环境配置
├── routes/
│ ├── __init__.py
│ ├── chat.py
│ ├── session.py
│ └── health.py
├── errors.py # 错误处理
└── .env # 环境变量(不要提交到Git)5.1 config.py
# config.py
import os
class Config:
SECRET_KEY = os.environ.get("SECRET_KEY", "dev-secret-key")
DEFAULT_MODEL = os.environ.get("DEFAULT_MODEL", "deepseek-v4-flash")
MAX_HISTORY = int(os.environ.get("MAX_HISTORY", 50))
class DevelopmentConfig(Config):
DEBUG = True
class ProductionConfig(Config):
DEBUG = False
config_map = {
"development": DevelopmentConfig,
"production": ProductionConfig,
}5.2 app.py
# app.py
import os
from flask import Flask
from config import config_map
def create_app():
env = os.environ.get("FLASK_ENV", "development")
app = Flask(__name__)
app.config.from_object(config_map[env])
app.config.from_prefixed_env()
# 注册蓝图
from routes.chat import chat_bp
from routes.session import session_bp
from routes.health import health_bp
app.register_blueprint(chat_bp, url_prefix="/api")
app.register_blueprint(session_bp, url_prefix="/api")
app.register_blueprint(health_bp)
# 注册错误处理
from errors import register_error_handlers
register_error_handlers(app)
return app
if __name__ == "__main__":
app = create_app()
app.run()5.3 在蓝图中访问配置
Blueprint中不能直接访问app.config,需要用Flask提供的current_app代理:
from flask import Blueprint, current_app
chat_bp = Blueprint("chat", __name__)
@chat_bp.route("/chat", methods=["POST"])
def chat():
model = current_app.config["DEFAULT_MODEL"]
return {"model": model}current_app会自动指向当前处理请求的应用实例,在请求之外访问会报错。
六、.env文件
开发阶段,可以用python-dotenv自动加载.env文件中的环境变量,不用每次手动export:
pip install python-dotenv创建.env文件:
FLASK_ENV=development
SECRET_KEY=dev-secret-key
DEFAULT_MODEL=deepseek-v4-flashFlask会自动读取项目根目录的.env文件(需要安装python-dotenv)。
注意:.env文件不要提交到Git。在.gitignore中加上:
.env七、总结
配置管理和App Factory是Flask项目从"能跑"到"能维护"的关键:
- 配置加载:
from_pyfile(Python文件)、from_object(类)、from_prefixed_env(环境变量) - 多环境:用类继承区分dev/prod,通过环境变量切换
- App Factory:把创建应用封装到
create_app()函数,方便测试和扩展 - current_app:在蓝图中通过代理访问应用配置
- .env文件:开发时自动加载环境变量,不要提交到Git
在下一篇文章中,我们将学习错误处理与日志——当你的Agent API出了问题,怎么快速定位和修复。